package by.gamedev.site.client.managed.request;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import com.google.gwt.requestfactory.shared.EntityProxy;

/**
 * A helper class for dealing with proxy types. Subclass it and override the various handle methods for type specific handling of proxy objects or
 * classes, then call {@link #process(Class)} or {@link #process(Object)}. Optionally use {#setResult} to set the return value of the {@link #process}
 * call.
 * 
 * <p>
 * Use {@link #getAll} for a set of all proxy types.
 * 
 * @param <T>
 *            the type to filter to
 */
public abstract class ApplicationEntityTypesProcessor<T> {

    /**
     * Return a set of all proxy types available to this application.
     */
    public static Set<Class<? extends EntityProxy>> getAll() {
        final Set<Class<? extends EntityProxy>> rtn = new HashSet<Class<? extends EntityProxy>>();

        // TODO rtn.add(ReportProxy.class);
        // TODO rtn.add(ExpenseProxy.class);
        // TODO rtn.add(EmployeeProxy.class);

        return Collections.unmodifiableSet(rtn);
    }

    private static void process(final ApplicationEntityTypesProcessor<?> processor, final Class<?> clazz) {
        // TODO
        /*
         * if(ReportProxy.class.equals(clazz) ) { processor.handleReport((ReportProxy) null); return; } if(ExpenseProxy.class.equals(clazz) ) {
         * processor.handleExpense((ExpenseProxy) null); return; } if(EmployeeProxy.class.equals(clazz) ) { processor.handleEmployee((EmployeeProxy)
         * null); return; }
         */
        processor.handleNonProxy(null);
    }

    private static void process(final ApplicationEntityTypesProcessor<?> processor, final Object proxy) {
        // TODO
        /*
         * if(proxy instanceof ReportProxy ) { processor.handleReport((ReportProxy) proxy); return; } if(proxy instanceof ExpenseProxy ) {
         * processor.handleExpense((ExpenseProxy) proxy); return; } if(proxy instanceof EmployeeProxy ) { processor.handleEmployee((EmployeeProxy)
         * proxy); return; }
         */
        processor.handleNonProxy(proxy);
    }

    private final T defaultValue;
    private T       result;

    /**
     * Create an instance with a null default value.
     */
    public ApplicationEntityTypesProcessor() {
        defaultValue = null;
    }

    /**
     * Create an instance with the given default value.
     * 
     * @param the
     *            value that will be returned by {@link #process} if {@link #setResult} is not called.
     */
    public ApplicationEntityTypesProcessor(final T defaultValue) {
        this.defaultValue = defaultValue;
    }

    /**
     * Called if {@link #process} is called with a non-proxy object. This default implementation does nothing.
     */
    public void handleNonProxy(final Object object) {
        // nothing
    }

    // TODO public abstract void handleReport(ReportProxy proxy);

    // TODO public abstract void handleExpense(ExpenseProxy proxy);

    // TODO public abstract void handleEmployee(EmployeeProxy proxy);

    /**
     * Call the handle method of the appropriate type, with a null argument. Note that this will not work as expected on the class objects returned by
     * the {@link #getClass()} method of a proxy object, due to limitations of GWT's metadata. It will only work with against class objects in the set
     * returned by {@link #getAll()}, or returned by {@link com.google.gwt.requestfactory.shared.RequestFactory#getClass(EntityProxy)} or
     * {@link com.google.gwt.requestfactory.shared.RequestFactory#getClass(String)}.
     * 
     * @param clazz
     *            the proxy type to resolve
     * @return the value provided via {@link #setResult}, or the default value
     */
    public T process(final Class<?> clazz) {
        setResult(defaultValue);
        ApplicationEntityTypesProcessor.process(this, clazz);
        return result;
    }

    /**
     * Process a proxy object
     * 
     * @param proxy
     *            the proxy to process
     * @return the value provided via {@link #setResult}, or the default value
     */
    public T process(final Object proxy) {
        setResult(defaultValue);
        ApplicationEntityTypesProcessor.process(this, proxy);
        return result;
    }

    /**
     * Set the value to return from a call to {@link #process(Class)} or {@link #process(Object)}.
     * 
     * @param result
     *            the value to return
     */
    protected void setResult(final T result) {
        this.result = result;
    }
}
